#include "Palace.h"
#include <Shlwapi.h>
#include <fstream>
#include <algorithm>
#include <sstream>

#pragma comment(lib, "shlwapi.lib")


sudoku::Cell::Cell() : Value(0)
{
}

sudoku::Cell::~Cell()
{
}

void sudoku::Cell::clear()
{
	Value = 0;
	Candidates.clear();
}

std::string sudoku::Cell::toJson()
{
	std::ostringstream oss;
	oss << "\"Value\":" << Value << ",";
	oss << "\"Candidates\":";
	oss << "[";

	bool first = true;

	for (auto i : Candidates)
	{
		if (!first)
			oss << ",";
		first = false;
		oss << i;
	}

	oss << "]";
	return oss.str();
}

sudoku::Board::Board()
{
}

sudoku::Board::~Board()
{
}

void sudoku::Board::clear()
{
	for (int i=0;i<81;i++)
	{
		_cell[i].clear();
	}
}

bool sudoku::Board::loadFromFile(const std::wstring & file)
{
	if (!PathFileExists(file.c_str()))
	{
		wprintf(L"File: %s dose not existed.\n", file.c_str());
		return false;
	}

	std::wifstream ifs;
	ifs.open(file, std::ios::in);
	
	if (!ifs.good())
	{
		wprintf(L"File: %s open failed.\n", file.c_str());
		return false;
	}

	int i = 0, t = 0;

	while (ifs >> t)
	{
		_cell[i].Value = t;
		i++;

		if (i >= 81)
			break;
	}

	ifs.close();
	return true;
}

bool sudoku::Board::calculate()
{
	bool ret = false;

	for (int i=0;i<81;i++)
	{
		ret = calculate(i);

		if (!ret)
			break;
	}

	return ret;
}

void sudoku::Board::showResult()
{
	for (int x=0;x<9;x++)
	{
		for (int y = 0; y < 9; y++)
		{
			Cell c = _cell[x*9+y];
			
			if (c.Value > 0 && c.Value <= 9)
			{
				printf("[%d, %d] Value: %d\n", x, y, c.Value);
			}
			else
			{
				printf("[%d, %d] Value: NULL candidates:[ ", x, y);
				for (auto i : c.Candidates)
					printf("%d, ", i);
				printf(" ]\n");
			}
		}
	}
}

std::string sudoku::Board::showResultAsJson() const
{
	std::ostringstream oss;
	oss << "[";
	bool first = true;

	for (int x = 0; x < 9; x++)
	{
		for (int y = 0; y < 9; y++)
		{
			if (!first)
			{
				oss << ",";
			}

			first = false;
			Cell c = _cell[x * 9 + y];
			oss << "{";
			oss << "\"X\":" << x << ",";
			oss << "\"Y\":" << y << ",";
			oss << c.toJson();
			oss << "}";
		}
	}

	oss << "]";
	return oss.str();
}

bool sudoku::Board::toXY(const int c, int & x, int & y)
{
	if (c < 0 || c > 80)
		return false;

	x = c / 9;
	y = c % 9;
	return true;
}

bool sudoku::Board::toPalace(const int x, const int y, int & px, int & py)
{
	if (x < 0 || x > 8)
		return false;

	if (y < 0 || y > 8)
		return false;

	px = x / 3;
	py = y / 3;
	return true;
}

bool sudoku::Board::calculate(const int c)
{
	if (c < 0 || c > 80)
		return false;

	if (_cell[c].Value > 0 && _cell[c].Value <= 9)
		return true;

	// three steps
	int x = -1, y = -1;
	bool succ = false;
	succ = toXY(c, x, y);

	if (!succ)
		return false;

	_cell[c].Candidates = {1,2,3,4,5,6,7,8,9};

	// 1. calculate line
	getCandidatesFromLine(x, _cell[c].Candidates);

	// 2. calculate column
	getCandidatesFromColumn(y, _cell[c].Candidates);

	// 3. calculate palace
	getCandidatesFromPalace(x, y, _cell[c].Candidates);
	return true;
}

bool sudoku::Board::getRow(const int x, std::vector<Cell*>& row)
{
	if (x < 0 || x > 8)
		return false;

	for (int i=0;i<9;i++)
	{
		row.push_back(&_cell[x*9 + i]);
	}

	return true;
}

bool sudoku::Board::getColumn(const int y, std::vector<Cell*>& col)
{
	if (y < 0 || y > 8)
		return false;

	for (int i=0;i<9;i++)
	{
		col.push_back(&_cell[i*9 + y]);
	}

	return true;
}

bool sudoku::Board::getPalace(const int px, const int py, std::vector<Cell*>& pal)
{
	if (px < 0 || px > 3)
		return false;

	if (py < 0 || py > 3)
		return false;

	int x = px * 3;
	int y = py * 3;

	pal.push_back(&_cell[x * 9 + y]);
	pal.push_back(&_cell[x * 9 + y + 1]);
	pal.push_back(&_cell[x * 9 + y + 2]);
	pal.push_back(&_cell[(x + 1) * 9 + y]);
	pal.push_back(&_cell[(x + 1) * 9 + y + 1]);
	pal.push_back(&_cell[(x + 1) * 9 + y + 2]);
	pal.push_back(&_cell[(x + 2) * 9 + y]);
	pal.push_back(&_cell[(x + 2) * 9 + y + 1]);
	pal.push_back(&_cell[(x + 2) * 9 + y + 2]);
	return true;
}

void sudoku::Board::getCandidatesFromLine(const int x, std::set<int>& cand)
{
	std::vector<Cell *> row;
	getRow(x, row);
	std::set<int> appered;
	std::vector<int> tmp(9);

	for (auto i : row)
	{
		if (i->Value > 0 && i->Value <= 9)
		{
			appered.insert(i->Value);
		}
	}

	std::set_difference(cand.begin(), cand.end(), appered.begin(), appered.end(), tmp.begin());
	cand.clear();
	
	for (auto i : tmp)
	{
		if (i != 0) cand.insert(i);
	}
}

void sudoku::Board::getCandidatesFromColumn(const int y, std::set<int>& cand)
{
	std::vector<Cell *> col;
	getColumn(y, col);
	std::set<int> appered;
	std::vector<int> tmp(9);

	for (auto i : col)
	{
		if (i->Value > 0 && i->Value <= 9)
		{
			appered.insert(i->Value);
		}
	}

	std::set_difference(cand.begin(), cand.end(), appered.begin(), appered.end(), tmp.begin());
	cand.clear();

	for (auto i : tmp)
	{
		if (i != 0) cand.insert(i);
	}
}

void sudoku::Board::getCandidatesFromPalace(const int x, const int y, std::set<int>& cand)
{
	int px = -1, py = -1;
	std::vector<Cell *> pal;
	bool succ = toPalace(x, y, px, py);
	std::set<int> appered;
	std::vector<int> tmp(9);

	if (!succ)
		return;

	getPalace(px, py, pal);

	for (auto i : pal)
	{
		if (i->Value > 0 && i->Value <= 9)
		{
			appered.insert(i->Value);
		}
	}

	std::set_difference(cand.begin(), cand.end(), appered.begin(), appered.end(), tmp.begin());
	cand.clear();

	for (auto i : tmp)
	{
		if (i != 0) cand.insert(i);
	}
}
